﻿using System;
using Nofs.Net.Common.Interfaces.Library;
using biz.ritter.javapi.util;
using Nofs.Net.Common.Interfaces.Domain;
using Db4objects.Db4o;
using biz.ritter.javapi.lang;

namespace Nofs.Net.nofs.Db4o
{

    public class Db4oStatMapper : IStatMapper
    {

        private IObjectContainer _db;
        private Map<UUID, LinkedList<IFileObjectStat>> _cache;
        private Db4oSaveThread _saveThread;
        private Class _fileObjectStatClass;

        public Db4oStatMapper(IObjectContainer db, Class fileObjectStatClass)
        {
            _db = db;
            _cache = null;
            _saveThread = null;
            _fileObjectStatClass = fileObjectStatClass;
        }

        public void DumpStatTables() //throws Exception 
        {
            LinkedList<UUID> list = new LinkedList<UUID>(Cache().keySet());
            foreach (UUID id in list)
            {
                LinkedList<IFileObjectStat> stats = Cache().get(id);
                foreach (IFileObjectStat stat in stats)
                {
                    Console.WriteLine(stat.toString());
                }
            }
        }

        private Map<UUID, LinkedList<IFileObjectStat>> Cache()
        {
            if (_cache == null)
            {
                _cache = new TreeMap<UUID, LinkedList<IFileObjectStat>>();
                lock (_db)
                {
                    foreach (object statObj in _db.Query(_fileObjectStatClass.GetType()))
                    {
                        IFileObjectStat stat = (IFileObjectStat)statObj;
                        LinkedList<IFileObjectStat> nameStatMap;
                        if (!_cache.containsKey(stat.ParentID()))
                        {
                            nameStatMap = new LinkedList<IFileObjectStat>();
                            _cache.put(stat.ParentID(), nameStatMap);
                        }
                        else
                        {
                            nameStatMap = _cache.get(stat.ParentID());
                        }
                        nameStatMap.add(stat);
                        SystemJ.err.println("Loaded: " + stat.ParentName() + " " + stat.ParentID().toString() + " " + stat.GetID().toString());
                    }
                }
                _saveThread = new Db4oSaveThread(_db);
                _saveThread.start();
            }
            return _cache;
        }

        //@Override
        public void CleanUp(bool cleanupManager) //throws Exception 
        {
            if (_saveThread != null)
            {
                _saveThread.Kill();
            }
            _db.Commit();
            _db.Close();
        }

        //@Override
        public void Delete(UUID id) //throws Exception 
        {
            if (Cache().containsKey(id))
            {
                foreach (IFileObjectStat stat in Cache().get(id))
                {
                    _saveThread.Delete(stat);
                }
                Cache().remove(id);
            }
        }

        public bool HasStat(UUID id, String name) //throws Exception 
        {
            if (Cache().containsKey(id))
            {
                return HasStat(Cache().get(id), name);
            }
            return false;
        }

        private bool HasStat(LinkedList<IFileObjectStat> stats, String name)
        {
            foreach (IFileObjectStat stat in stats)
            {
                if (stat.ParentName().compareTo(name) == 0)
                {
                    return true;
                }
            }
            return false;
        }

        //@Override
        public IFileObjectStat Load(UUID id, String name) //throws Exception 
        {
            Console.WriteLine("Load(" + id.toString() + "," + name + ")");
            if (!HasStat(id))
            {
                throw new System.Exception("could not load stat for ID = " + id.toString() + " name = " + name);
            }
            foreach (IFileObjectStat stat in Cache().get(id))
            {
                if (stat.ParentName().compareTo(name) == 0)
                {
                    return stat;
                }
            }
            throw new System.Exception("could not load stat for ID = " + id.toString() + " name = " + name);
        }

        public bool HasStat(UUID id) //throws Exception
        {
            return Cache().containsKey(id);
        }

        //@Override
        public void Save(IFileObjectStat stat) //throws Exception 
        {
            if (stat.IsNew())
            {
                stat.SetID(UUID.randomUUID());
                stat.SetIsNew();
            }
            LinkedList<IFileObjectStat> nameStatMap;
            if (Cache().containsKey(stat.ParentID()))
            {
                nameStatMap = Cache().get(stat.ParentID());
            }
            else
            {
                nameStatMap = new LinkedList<IFileObjectStat>();
                Cache().put(stat.ParentID(), nameStatMap);
            }
            if (!HasStat(nameStatMap, stat.ParentName()))
            {
                nameStatMap.add(stat);
            }
            Console.WriteLine("saving stat with id = " + stat.GetID());
            _saveThread.Enqueue(stat);
            stat.SetIsNotNew();
        }

        //@Override
        public void Rename(UUID id, String oldName, String newName) //throws Exception 
        {
            IFileObjectStat stat = Load(id, oldName);
            stat.SetParentName(newName);
            Save(stat);
        }
    }
}
